﻿using Newtonsoft.Json;
using StackExchange.Redis;

namespace RedisDemo.Redis;

public class RedisCacheManager : ICacheManager
{
    private readonly IRedisConnectionWrapper _connectionWrapper;

    private readonly IDatabase _db;

    /// <summary>
    /// 
    /// </summary>
    /// <param name="connectionWrapper"></param>
    public RedisCacheManager(IRedisConnectionWrapper connectionWrapper)
    {
        _connectionWrapper = connectionWrapper;
        _db = _connectionWrapper.GetDatabase();
    }

    /// <summary>
    /// 清理全部
    /// </summary>
    public void Clear()
    {
        foreach (var endPoint in _connectionWrapper.GetEndPoints())
        {
            var server = _connectionWrapper.GetServer(endPoint);
            var keys = server.Keys(_db.Database);
            _db.KeyDelete(keys.ToArray());
        }
    }

    public long StringIncrement(string key, long value)
    {
        long result = _db.StringIncrement(key, value);
        return result;
    }

    /// <summary>
    /// 获取缓存
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="key"></param>
    /// <returns></returns>
    public T Get<T>(string key)
    {
        var serializedItem = _db.StringGet(key);

        if (!serializedItem.HasValue)
            return default(T);
        var item = JsonConvert.DeserializeObject<T>(serializedItem);
        if (item == null)
            return default(T);
        return item;
    }

    /// <summary>
    /// 检查缓存是否存在
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public bool IsSet(string key)
    {
        return _db.KeyExists(key);
    }

    public async Task<bool> IsSetAsync(string key)
    {
        return await _db.KeyExistsAsync(key);
    }

    /// <summary>
    /// 移除缓存
    /// </summary>
    /// <param name="key"></param>
    public void Remove(string key)
    {
        _db.KeyDelete(key);
    }

    /// <summary>
    /// 匹配删除多项
    /// </summary>
    /// <param name="pattern"></param>
    public void RemoveByPattern(string pattern)
    {
        foreach (var endPoint in _connectionWrapper.GetEndPoints())
        {
            var server = _connectionWrapper.GetServer(endPoint);
            var keys = server.Keys(_db.Database, $"*{pattern}*");
            _db.KeyDelete(keys.ToArray());
        }
    }

    /// <summary>
    /// 设置缓存
    /// </summary>
    /// <param name="key"></param>
    /// <param name="data"></param>
    /// <param name="cacheTime"></param>
    public void Set(string key, object data, int cacheTime)
    {
        if (data == null)
            return;

        var expiresIn = TimeSpan.FromMinutes(cacheTime);

        _db.StringSet(key, data.ToString(), expiresIn);
    }

    public async void SetAsync(string key, object data, int cacheTime)
    {
        if (data == null)
            return;

        var expiresIn = TimeSpan.FromMinutes(cacheTime);

        await _db.StringSetAsync(key, data.ToString(), expiresIn);
    }

    /// <summary>
    /// 设置缓存
    /// </summary>
    /// <param name="key"></param>
    /// <param name="data"></param>
    /// <param name="cacheTime"></param>
    public bool SetValue<T>(string key, T data, int cacheTime)
    {
        if (data == null)
            return false;

        var expiresIn = TimeSpan.FromMinutes(cacheTime);

        return _db.StringSet(key, JsonConvert.SerializeObject(data), expiresIn);
    }

    public async Task<bool> SetValueAsync<T>(string key, T data, int cacheTime)
    {
        if (data == null)
            return false;

        var expiresIn = TimeSpan.FromMinutes(cacheTime);

        return await _db.StringSetAsync(key, JsonConvert.SerializeObject(data), expiresIn);
    }

    /// <summary>
    /// 插入List
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="key"></param>
    /// <param name="value"></param>
    public void ListSet<T>(string key, List<T> value)
    {
        foreach (var single in value)
        {
            var s = JsonConvert.SerializeObject(single);
            _db.ListRightPush(key, s); //要一个个的插入
        }
    }

    /// <summary>
    /// 插入ListAsync
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="key"></param>
    /// <param name="value"></param>
    public async Task ListSetAsync<T>(string key, List<T> value)
    {
        foreach (var single in value)
        {
            var s = JsonConvert.SerializeObject(single);
            await _db.ListRightPushAsync(key, s); //要一个个的插入
        }
    }

    /// <summary>
    /// 获取List
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="key"></param>
    /// <returns></returns>
    public List<T> ListGet<T>(string key)
    {
        if (!IsSet(key)) return new List<T>();
        //需要逐个反序列化成实体
        var vList = _db.ListRange(key);
        List<T> result = new List<T>();
        foreach (var item in vList)
        {
            var model = JsonConvert.DeserializeObject<T>(item); //反序列化
            result.Add(model);
        }
        return result;
    }

    public async Task<List<T>> ListGetAsync<T>(string key)
    {
        if (!IsSet(key)) return new List<T>();
        //需要逐个反序列化成实体
        var vList =await _db.ListRangeAsync(key);
        List<T> result = new List<T>();
        foreach (var item in vList)
        {
            var model = JsonConvert.DeserializeObject<T>(item); //反序列化
            result.Add(model);
        }
        return result;
    }

    /// <summary>
    /// 删除List指定值
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public long LisRemove<T>(string key, T value)
    {
        return _db.ListRemove(key, JsonConvert.SerializeObject(value));
    }

    /// <summary>
    /// 删除List指定值Async
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public async Task<long> LisRemoveAsync<T>(string key, T value)
    {
        return await _db.ListRemoveAsync(key, JsonConvert.SerializeObject(value));
    }

    /// <summary>
    /// 添加一个值到Key
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <param name="score">排序分数，为空将获取集合中最大score加1</param>
    /// <returns></returns>
    public async Task<bool> SortedSetAddAsync<T>(string key, T value, double? score = null)
    {
        double scoreNum = score ?? _GetScore(key) + 1;
        return await _db.SortedSetAddAsync(key, ConvertJson<T>(value), scoreNum);
    }

    /// <summary>
    /// 添加一个集合到Key
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <param name="score">排序分数，为空将获取集合中最大score加1</param>
    /// <returns></returns>
    public async Task<long> SortedSetAddAsync<T>(string key, List<T> value, double? score = null)
    {
        double scoreNum = score ?? _GetScore(key) + 1;
        SortedSetEntry[] rValue = value.Select(o => new SortedSetEntry(ConvertJson<T>(o), scoreNum++)).ToArray();
        return await _db.SortedSetAddAsync(key, rValue);
    }

    /// <summary>
    ///  删除元素
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public async Task<bool> SortedSetRemoveAsync<T>(string key, T value)
    {
        return await _db.SortedSetRemoveAsync(key, ConvertJson<T>(value));
    }

    /// <summary>
    ///  删除元素
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public async Task<long> SortedSetRemoveRangeByScoreAsync<T>(string key, double start, double stop)
    {
        return await _db.SortedSetRemoveRangeByScoreAsync(key, start, stop);
    }

    /// <summary>
    ///  根据排序分数Score，删除从 start 开始的 stop 条数据
    /// </summary>
    /// <param name="key"></param>
    /// <param name="start"></param>
    /// <param name="stop"></param>
    /// <param name="exclude"></param>
    /// <returns></returns>
    public async Task<long> SortedSetRemoveRangeByScoreAsync(string key, double start, double stop, Exclude exclude = Exclude.None)
    {
        return await _db.SortedSetRemoveRangeByScoreAsync(key, start, stop, exclude);
    }

    /// <summary>
    ///  根据Score排序 获取从 start 开始的 stop 条数据
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="key"></param>
    /// <param name="start"></param>
    /// <param name="stop"></param>
    /// <param name="de"></param>
    /// <param name="desc"></param>
    /// <returns></returns>
    public async Task<List<T>> SortedSetRangeByRankAsync<T>(string key, long start = 0, long stop = -1, bool desc = false)
    {
        Order orderBy = desc ? Order.Descending : Order.Ascending;
        var rValue = await _db.SortedSetRangeByRankAsync(key, start, stop, orderBy);
        return ConvetList<T>(rValue);
    }

    /// <summary>
    /// 加锁执行方法（无返回值）
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="lockKey">锁key</param>
    /// <param name="act">获取锁成功后执行的委托</param>
    /// <param name="expiry">锁到期时间，超时自动解锁</param>
    /// <param name="waitTime">最大等待时间（空值代表不等待）</param>
    /// <returns>执行结果</returns>
    public bool ExecuteWithLock(string lockKey, Action act, TimeSpan? expiry = null, TimeSpan? waitTime = null)
    {
        DateTime start = DateTime.Now;
        RedisValue token = Environment.MachineName;

        // 超时时间默认10秒
        TimeSpan expiryTime = TimeSpan.FromSeconds(10);
        if (expiry != null)
        {
            expiryTime = (TimeSpan)expiry;
        }

        bool result;
        while (!(result = _db.LockTake(lockKey, token, expiryTime)))
        {
            if (waitTime == null || DateTime.Now - start > waitTime.Value)
            {
                break;
            }

            Thread.Sleep(50);
        }

        if (result)
        {
            try
            {
                act();
            }
            finally
            {
                _db.LockRelease(lockKey, token);
            }
        }

        return result;
    }

    /// <summary>
    /// 加锁执行方法（带返回值）
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="lockKey">锁key</param>
    /// <param name="func">获取锁成功后执行的委托</param>
    /// <param name="value">执行结果</param>
    /// <param name="expiry">锁到期时间，超时自动解锁</param>
    /// <param name="waitTime">最大等待时间（空值代表不等待）</param>
    /// <returns>执行结果</returns>
    public bool ExecuteWithLock<T>(string lockKey, Func<T> func, out T value, TimeSpan? expiry = null, TimeSpan? waitTime = null)
    {
        value = default(T);
        DateTime start = DateTime.Now;
        RedisValue token = Environment.MachineName;

        // 超时时间默认10秒
        TimeSpan expiryTime = TimeSpan.FromSeconds(10);
        if (expiry != null)
        {
            expiryTime = (TimeSpan)expiry;
        }

        bool result;
        while (!(result = _db.LockTake(lockKey, token, expiryTime)))
        {
            if (waitTime == null || DateTime.Now - start > waitTime.Value)
            {
                break;
            }

            Thread.Sleep(50);
        }

        if (result)
        {
            try
            {
                value = func();
            }
            finally
            {
                _db.LockRelease(lockKey, token);
            }
        }

        return result;
    }

    #region 辅助方法

    /// <summary>
    /// 获取指定Key中最大Score值,
    /// </summary>
    /// <param name="key">key名称，注意要先添加上Key前缀</param>
    /// <returns></returns>
    private double _GetScore(string key)
    {
        var rValue = _db.SortedSetRangeByRankWithScores(key, 0, stop: -1, Order.Descending).FirstOrDefault();
        return rValue.Score;
    }

    /// <summary>
    /// 将对象转换成string字符串
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="value"></param>
    /// <returns></returns>
    protected string ConvertJson<T>(T value)
    {
        // 解决导航属性问题序列化失败问题
        string result = value is string ? value.ToString() : JsonConvert.SerializeObject(value, Formatting.None,
            new JsonSerializerSettings()
            {
                ReferenceLoopHandling = ReferenceLoopHandling.Ignore
            });

        return result;
    }
    /// <summary>
    /// 将值反系列化成对象
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="value"></param>
    /// <returns></returns>
    protected T ConvertObj<T>(RedisValue value)
    {
        return value.IsNullOrEmpty ? default(T) : JsonConvert.DeserializeObject<T>(value);
    }


    /// <summary>
    /// 将值反系列化成对象集合
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="values"></param>
    /// <returns></returns>
    protected List<T> ConvetList<T>(RedisValue[] values)
    {
        List<T> result = new List<T>();
        foreach (var item in values)
        {
            var model = ConvertObj<T>(item);
            result.Add(model);
        }
        return result;
    }

    /// <summary>
    /// 将string类型的Key转换成 <see cref="RedisKey"/> 型的Key
    /// </summary>
    /// <param name="redisKeys"></param>
    /// <returns></returns>
    protected RedisKey[] ConvertRedisKeys(List<string> redisKeys) => redisKeys.Select(redisKey => (RedisKey)redisKey).ToArray();

    /// <summary>
    /// 将string类型的Key转换成 <see cref="RedisKey"/> 型的Key
    /// </summary>
    /// <param name="redisKeys"></param>
    /// <returns></returns>
    protected RedisKey[] ConvertRedisKeys(params string[] redisKeys) => redisKeys.Select(redisKey => (RedisKey)redisKey).ToArray();


    /// <summary>
    /// 将值集合转换成RedisValue集合
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="redisValues"></param>
    /// <returns></returns>
    protected RedisValue[] ConvertRedisValue<T>(params T[] redisValues) => redisValues.Select(o => (RedisValue)ConvertJson<T>(o)).ToArray();

    #endregion 辅助方法
}
